/*
 * 文件名：BeanCopys.java
 * 版权：MoonBox 
 * 修改人：Peter
 * 修改时间：2018年10月15日
 * 修改内容：新增
 */
package com.renzhebiao.lkang.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.renzhebiao.lkang.exception.ExceptionTest;

import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter;

/**
 * <p>
 * 
 * @author Peter
 * @since 1.0.0
 */
public class BeanCopys {

    /**
     * the beanCopierMap
     */
    private static final ConcurrentMap<String, BeanCopier> beanCopierMap = new ConcurrentHashMap<>();

    private static final Map<Class<?>, Class<?>>           primitiveMap  = new HashMap<>(9);

    static {
        primitiveMap.put(String.class, String.class);
        primitiveMap.put(Boolean.class, boolean.class);
        primitiveMap.put(Byte.class, byte.class);
        primitiveMap.put(Character.class, char.class);
        primitiveMap.put(Double.class, double.class);
        primitiveMap.put(Float.class, float.class);
        primitiveMap.put(Integer.class, int.class);
        primitiveMap.put(Long.class, long.class);
        primitiveMap.put(Short.class, short.class);
        primitiveMap.put(BigDecimal.class, BigDecimal.class);
    }

    /**
     * @description 判断基本类型
     * @see java.lang.String#TYPE
     * @see java.lang.Boolean#TYPE
     * @see java.lang.Character#TYPE
     * @see java.lang.Byte#TYPE
     * @see java.lang.Short#TYPE
     * @see java.lang.Integer#TYPE
     * @see java.lang.Long#TYPE
     * @see java.lang.Float#TYPE
     * @see java.lang.Double#TYPE
     * @see java.lang.Boolean#TYPE
     * @see char#TYPE
     * @see byte#TYPE
     * @see short#TYPE
     * @see int#TYPE
     * @see long#TYPE
     * @see float#TYPE
     * @see double#TYPE
     * @param clazz
     * @return boolean
     */
    public static boolean isPrimitive(Class<?> clazz) {
        if (primitiveMap.containsKey(clazz)) {
            return true;
        }
        return clazz.isPrimitive();
    }

    /**
     * 对象拷贝工具方法
     * 
     * @param s 数据
     * @param descType 转换对象的class
     * @return 转换后的对象
     * @param <S> S
     * @param <D> d
     */
    public static <S, D> D maping(S s, Class<D> descType) {
        if (s == null) {
            return null;
        }
        // 动态生成用于复制的类,false为不使用Converter类
        BeanCopier copier = BeanCopier.create(s.getClass(), descType, false);
        D target;
        try {
            target = descType.newInstance();
        } catch (InstantiationException e) {
            throw new ExceptionTest("构建错误");
        } catch (IllegalAccessException e) {
            throw new ExceptionTest("构建错误");
        }
        // 执行source到target的属性复制
        copier.copy(s, target, null);
        return target;
    }

    /**
     * List对象拷贝工具方法,性能测试双核，百万次1.7秒
     * 
     * @param source 数据
     * @param destType 转换对象的class
     * @return 转换后的对象
     * @param <S> S
     * @param <D> d
     */
    public static <S, D> List<D> maping(final List<S> sources, final Class<D> destType) {
        if (EmptyTools.isNull(sources)) {
            return null;
        }
        List<D> targets = new ArrayList<>(sources.size());
        for (S source : sources) {
            D target = maping(source, destType);
            targets.add(target);
        }
        return targets;
    }

    /**
     * 对象值复制.
     * 
     * @param source 复制源对象.
     * @param target 复制输出对象.
     */
    public static void maping(Object source, Object target) {
        if (source == null) {
            return;
        }
        // 动态生成用于复制的类,false为不使用Converter类
        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        // 执行source到target的属性复制
        copier.copy(source, target, null);
    }

    /**
     * 支持两边对象里面的属性类型不一致的拷贝方式，更高级，一般建议使用{@link mapping}
     * <p>
     * 性能测试双核，百万次2.7秒
     * <p>
     * 
     * @param source
     * @param target
     * @return
     * @return T
     */
    public static <S, T> T mappingAll(S source, Class<T> target) {
        if (source == null) {
            return null;
        }
        T ret;
        try {
            ret = target.newInstance();
        } catch (InstantiationException e) {
            throw new ExceptionTest("转换异常");
        } catch (IllegalAccessException e) {
            throw new ExceptionTest("转换异常");
        }
        BeanCopier beanCopier = getBeanCopier(source.getClass(), target);
        beanCopier.copy(source, ret, new DeepCopyConverter(target));
        return ret;
    }

    /**
     * List对象拷贝工具方法,支持两边对象里面的属性类型不一致的拷贝方式，更高级，一般建议使用{@link mapping}
     * <p>
     * 性能测试双核，百万次2.7秒
     * <p>
     * 暂时还不支持泛型
     * 
     * @param source 数据
     * @param destType 转换对象的class
     * @return 转换后的对象
     * @param <S> S
     * @param <D> d
     */
    public static <S, D> List<D> mappingAll(final List<S> sources, final Class<D> destType) {
        if (EmptyTools.isNull(sources)) {
            return null;
        }
        List<D> targets = new ArrayList<>(sources.size());
        for (S source : sources) {
            D target = mappingAll(source, destType);
            targets.add(target);
        }
        return targets;
    }

    /**
     * @description 获取方法返回值类型
     * @param tartget
     * @param fieldName
     * @return
     * @return Class<?>
     */
    public static Class<?> getElementType(Class<?> tartget, String fieldName) {
        Class<?> elementTypeClass = null;
        try {
            Type type = tartget.getDeclaredField(fieldName).getGenericType();
            ParameterizedType t = (ParameterizedType) type;
            String classStr = t.getActualTypeArguments()[0].toString().replace("class ", "");
            elementTypeClass = Thread.currentThread().getContextClassLoader().loadClass(classStr);
        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException e) {
            throw new RuntimeException("get fieldName[" + fieldName + "] error", e);
        }
        return elementTypeClass;
    }

    @SuppressWarnings("all")
    public static class DeepCopyConverter implements Converter {

        private Class<?> target;

        public DeepCopyConverter(Class<?> target) {
            this.target = target;
        }

        @Override
        public Object convert(Object value, Class targetClazz, Object methodName) {
            if (value instanceof List) {// 属性的类型是list
                List values = (List) value;
                List retList = new ArrayList<>(values.size());
                for (final Object source : values) {
                    Class clazz = getElementType(target, (methodName.toString().replace("set", "")).toLowerCase());
                    try {
                        retList.add(BeanCopys.mappingAll(source, clazz));
                    } catch (Exception e) {
                        // log.error("Failed to convert", e);
                        throw new ExceptionTest("转换异常");
                    }
                }
                return retList;
            } else if (value instanceof Map) {
                // 暂时用不到，后续有需要再补充
            } else if (!isPrimitive(targetClazz)) {
                try {
                    return BeanCopys.mappingAll(value, targetClazz);
                } catch (Exception e) {
                    // log.error("Failed to convert", e);
                    throw new ExceptionTest("转换异常");

                }
            }
            return value;
        }
    }

    /**
     * @description 获取BeanCopier
     * @param source
     * @param target
     * @return
     * @return BeanCopier
     */
    public static BeanCopier getBeanCopier(Class<?> source, Class<?> target) {
        String beanCopierKey = generateBeanKey(source, target);
        if (beanCopierMap.containsKey(beanCopierKey)) {
            return beanCopierMap.get(beanCopierKey);
        } else {
            BeanCopier beanCopier = BeanCopier.create(source, target, true);
            beanCopierMap.putIfAbsent(beanCopierKey, beanCopier);
        }
        return beanCopierMap.get(beanCopierKey);
    }

    /**
     * @description 生成两个类的key
     * @param source
     * @param target
     * @return
     * @return String
     */
    public static String generateBeanKey(Class<?> source, Class<?> target) {
        return source.getName() + "@" + target.getName();
    }

    // --------------Map Copy Start

    /**
     * 对象转Map
     * 
     * @param source
     * @return
     */
    public static Map<String, Object> toMap(Object source) {
        if (source == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(source.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = getter != null ? getter.invoke(source) : null;
                map.put(key, value);
            }
        } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException e) {
            // log.error("Failed to toMap", e);
            // throw new ConversionException(StatusCode.ServerError.CONVERT_FAILED);
            throw new ExceptionTest("转换异常");
        }

        return map;
    }

    /**
     * Map转Object
     * 
     * @param map
     * @param clazz
     * @return
     */
    public static <D> D toObj(Map<String, Object> map, Class<D> clazz) {
        if (map == null) {
            return null;
        }
        D obj = null;
        try {
            obj = clazz.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method method = property.getWriteMethod();
                if (method != null) {
                    method.invoke(obj, map.get(property.getName()));
                }
            }
        } catch (InstantiationException | IllegalAccessException | IntrospectionException | IllegalArgumentException
                | InvocationTargetException e) {
            // log.error("Failed to toObj", e);
            // throw new ConversionException(StatusCode.ServerError.CONVERT_FAILED);
            throw new ExceptionTest("转换异常");
        }
        return obj;
    }

    /**
     * 转List对象
     * 
     * @param sources maps
     * @param clazz 类型
     * @return List对象
     */
    public static <D> List<D> toObj(final List<Map<String, Object>> sources, final Class<D> clazz) {
        if (EmptyTools.isNull(sources)) {
            return null;
        }
        List<D> targets = new ArrayList<>(sources.size());
        for (Map<String, Object> source : sources) {
            targets.add(toObj(source, clazz));
        }
        return targets;
    }

    /**
     * 转List<Map>
     * 
     * @param source list对象
     * @return Map<String, Object>
     */
    public static List<Map<String, Object>> toMap(final List<Object> sources) {
        if (EmptyTools.isNull(sources)) {
            return null;
        }
        List<Map<String, Object>> targets = new ArrayList<>(sources.size());
        for (Object source : sources) {
            targets.add(toMap(source));
        }
        return targets;
    }
}
